package com.zpark.uaa.service.config;

import com.zpark.uaa.service.integration.RestOAuth2WebResponseExceptionTranslator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
// import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;


@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private TokenStore tokenStore;

	@Autowired
	private JwtAccessTokenConverter accessTokenConverter;


	@Autowired
	private ClientDetailsService clientDetailsService;

	@Autowired
	private AuthorizationCodeServices authorizationCodeServices;

	@Autowired
	private AuthenticationManager authenticationManager;


	/**
	 * 配置客户端详情服务。
	 * 该方法重写了{
	 * org.springframework.security.oauth2.config.
	 * annotation.configurers.ClientDetailsService
	 * Configurer#configure(ClientDetailsService)}方法，
	 * 用于指定客户端详情的来源服务。
	 *
	 * @param clients 客户端详情服务配置器，用于构建和配置客户端详情服务。
	 * @throws Exception 如果配置过程中发生错误，则抛出异常。
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
	    // 使用提供的客户端详情服务实例来配置客户端详情
	    clients.withClientDetails(clientDetailsService);
	}

	/**
	 * 2.配置令牌服务(token services)
	 */
    @Bean
    public AuthorizationServerTokenServices tokenService() {
        System.err.println("配置令牌服务(token services)");

        // 创建默认的令牌服务实例
        DefaultTokenServices service = new DefaultTokenServices();
        // 设置客户端详情服务
        service.setClientDetailsService(clientDetailsService);
        // 启用刷新令牌的支持
        service.setSupportRefreshToken(true);
        // 设置令牌存储方式
        service.setTokenStore(tokenStore);

        // 配置令牌增强器链，用于增强令牌的信息
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(
				Arrays.asList(/*tokenEnhancer(),*/ accessTokenConverter));
        service.setTokenEnhancer(tokenEnhancerChain);

        // 设置访问令牌和刷新令牌的有效期
        service.setAccessTokenValiditySeconds(7200); // 访问令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天

        return service;
    }




	/**
	 * 配置OAuth授权服务器端点。
	 *
	 * @param endpoints 授权服务器端点的配置器，允许定制授权服务器的行为。
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
	    System.err.println("配置OAuth授权服务器端点: configure()");

	    // 设置认证管理器、授权码服务、令牌服务以及路径映射。
	    endpoints.authenticationManager(authenticationManager)
	            .authorizationCodeServices(authorizationCodeServices)
	            .tokenServices(tokenService())
	            .pathMapping("/oauth/confirm_access", "/confirm_access")
	            .pathMapping("/oauth/error", "/oauth_error")
	            // 仅允许POST方法访问令牌端点。
	            .allowedTokenEndpointRequestMethods(HttpMethod.POST)
	            // 设置异常转换器，以统一处理OAuth的异常。
	            .exceptionTranslator(new RestOAuth2WebResponseExceptionTranslator());
	}

	/**
	 * 4.配置令牌端点(Token Endpoint)的安全约束
	 * 配置OAuth2授权服务器的安全设置。
	 *
	 * @param security 用于配置授权服务器安全性的
	 *                    AuthorizationServerSecurityConfigurer实例
	 */
	public void configure(AuthorizationServerSecurityConfigurer security){
	    // 允许所有的token访问
	    security
	            .tokenKeyAccess("permitAll()")
	            // 允许所有的token检查访问
	            .checkTokenAccess("permitAll()")
	            // 允许表单认证方式登录
	            .allowFormAuthenticationForClients()
	    ;
	}

}
